/*******************************************************************************
 * Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Marc R. Hoffmann - initial API and implementation
 *
 *******************************************************************************/
package org.jacoco.core.internal.flow;

import java.util.Map;

import org.jacoco.core.analysis.CoverageBuilder;
import org.jacoco.core.internal.diff.ClassInfo;
import org.jacoco.core.internal.diff.MethodInfo;
import org.jacoco.core.internal.instr.InstrSupport;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.commons.AnalyzerAdapter;

/**
 * A {@link org.objectweb.asm.ClassVisitor} that calculates probes for every
 * method.
 */
public class ClassProbesAdapter extends ClassVisitor implements
        IProbeIdGenerator {

    private static final MethodProbesVisitor EMPTY_METHOD_PROBES_VISITOR = new MethodProbesVisitor() {
    };

/*
	private static final Map<String, Integer> history = new HashMap<>();
*/


    private final ClassProbesVisitor cv;

    private final boolean trackFrames;

    private int counter = 0;

    private String name;

    /**
     * Creates a new adapter that delegates to the given visitor.
     *
     * @param cv
     *            instance to delegate to
     * @param trackFrames
     *            if <code>true</code> stackmap frames are tracked and provided
     */
    public ClassProbesAdapter(final ClassProbesVisitor cv,
            final boolean trackFrames) {
        super(InstrSupport.ASM_API_VERSION, cv);
        this.cv = cv;
        this.trackFrames = trackFrames;
    }

    @Override
    public void visit(final int version, final int access, final String name,
            final String signature, final String superName,
            final String[] interfaces) {
        this.name = name;
        super.visit(version, access, name, signature, superName, interfaces);
    }

    @Override
    public final MethodVisitor visitMethod(final int access, final String name,
            final String desc, final String signature, final String[] exceptions) {
        final MethodProbesVisitor methodProbes;
        final MethodProbesVisitor mv = cv.visitMethod(access, name, desc,
                signature, exceptions);
        //	增量计算覆盖率
        if (mv != null && isContainsMethod(name, CoverageBuilder.classInfos)) {
            methodProbes = mv;
        } else {
            // We need to visit the method in any case, otherwise probe ids
            // are not reproducible
            methodProbes = EMPTY_METHOD_PROBES_VISITOR;
        }
        return new MethodSanitizer(null, access, name, desc, signature,
                exceptions) {

            @Override
            public void visitEnd() {
                super.visitEnd();
                LabelFlowAnalyzer.markLabels(this);
                final MethodProbesAdapter probesAdapter = new MethodProbesAdapter(
                        methodProbes, ClassProbesAdapter.this);
                if (trackFrames) {
                    final AnalyzerAdapter analyzer = new AnalyzerAdapter(
                            ClassProbesAdapter.this.name, access, name, desc,
                            probesAdapter);
                    probesAdapter.setAnalyzer(analyzer);
                    methodProbes.accept(this, analyzer);
                } else {
                    methodProbes.accept(this, probesAdapter);
                }
            }
        };
    }

    @Override
    public void visitEnd() {
        cv.visitTotalProbeCount(counter);
        super.visitEnd();
    }

    // === IProbeIdGenerator ===

    public int nextId() {
        return counter++;
    }

    private boolean isContainsMethod(String currentMethod, Map<String, ClassInfo> classInfos) {
        if (classInfos == null || classInfos.isEmpty()) {
            return true;
        }
        String currentClassName = name.replaceAll("/", ".");
        ClassInfo classInfo = classInfos.get(currentClassName);
        if (classInfo == null) {
            return false;
        }
        Map<String, MethodInfo> methodInfos = classInfo.getMethodInfos();
        if (methodInfos == null) {
            return false;
        }
        return methodInfos.containsKey(currentMethod);
    }
}
